
Alkaline.Module = function() {
	this.supportedArguments = {};
	this.argumentDefaults = {};
}

Alkaline.Module.prototype.initModule = function(compileResult) {
	this.compileResult = compileResult;
	var fn = function() {};
	fn.prototype = this.argumentDefaults;
	this.arguments = new fn();	// inherit the defaults
	this.argumentsList = [];
}

Alkaline.Module.prototype.run = function(ast) {};

Alkaline.Module.prototype.registerSupportedArgument = function(argumentName, shortName, nrParams, def) {
	if (!nrParams)
		nrParams = 0;
	this.supportedArguments[argumentName] = 
	this.supportedArguments[shortName] = {
		fullName: argumentName,
		shortName: shortName,
		nrParams: nrParams
	}
	this.argumentDefaults[argumentName] = def;
}

Alkaline.Module.prototype.supportsArgument = function(argName) {
	return this.supportedArguments.hasOwnProperty(argName);
}

Alkaline.Module.prototype.getArgumentArity = function(argName) {
	return this.supportedArguments[argName].nrParams;
}

Alkaline.Module.prototype.setArgument = function(argName, param) {
	argName = this.supportedArguments[argName].fullName;
	if (!this.arguments.hasOwnProperty(argName))
		this.argumentsList.push(argName);
	if (!this.supportedArguments[argName].nrParams && (arguments.length == 1 || (param instanceof Array && param.length == 0)))
		param = !this.arguments[argName];
	this.arguments[argName] = param;
}

Alkaline.Module.prototype.DFS = function(ast, tokenTypes, callback) {
	if (tokenTypes)
		if (!(tokenTypes instanceof Array))
			tokenTypes = [tokenTypes];
		else if (!tokenTypes.length) {
			// nothing to process
			this.compileResult.stdWarn("[Alkaline.Module->DFS] Bad call: <tokenTypes> is an empty list!");
			return;
		}
	var root = ast;
	while (true) {
		if (!tokenTypes || tokenTypes.contains(ast.token.type))
			if (callback.call(this, ast) === this.stopSearch)
				return;
		if (ast.children && ast.children.length)
			ast = ast.children[0];
		else {
			while (ast.parent && ast.childIndex === ast.parent.children.length-1 && ast !== root)
				ast = ast.parent;
			if (ast === root) break;
			ast = ast.parent.children[ast.childIndex+1];
		}
	}
}
Alkaline.Module.prototype.stopSearch = {};

Alkaline.Module.prototype.error = function(code, text, reference) {
	if (reference instanceof org.antlr.runtime.tree.Tree)
		reference = reference.token;
	if (reference instanceof org.antlr.runtime.Token)
		reference = [ reference.line, reference.charPositionInLine+1 ];
	var location = '';
	if (reference)
		location = this.compileResult.getLocation(reference[0], reference[1]);
	this.compileResult._error(this.moduleName + ' Error E' + code + ' ' + location + ': ' + text);
}

Alkaline.Modules.newModule = function(name, initializer) {
	var module = Alkaline.Modules[name] = function(compileResult) {
		if (initializer) {
			initializer.call(module.prototype);
			initializer = null;
		}
		this.initModule(compileResult);
	}
	module.prototype = new Alkaline.Module();
	module.prototype.moduleName = name;
	Alkaline.Modules[name.toLowerCase()] = module;
};
